#include <GL/glew.h>
#include <GL/glut.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/freeglut.h>
#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <cmath>
#include <cstdlib>
#include <string.h>
#include <assert.h>
#include <math.h>
#include "CInitGL.h"
#include "math_3d.h"
#include "CShaders.h"
#include "CPipeline.h"
#include "Ccamera.h"


#define WINDOW_WIDTH 1024
#define WINDOW_HEIGHT 768


GLuint CInitGL::gScaleLocation;
CInitGL *CInitGL::instance = 0;

GLuint CShaders::gWorldLocation;
GLuint CInitGL::VBO;
GLuint CInitGL::IBO;
GLuint CInitGL::gWVPLocation;

Ccamera* pGameCamera = NULL;


GLfloat xrot;            // x rotation
GLfloat yrot;            // y rotation
GLfloat xspeed;          // x rotation speed
GLfloat yspeed;          // y rotation speed

GLfloat walkbias = 0;
GLfloat walkbiasangle = 0;

GLfloat lookupdown = 0.0;
const float piover180 = 0.0174532925f;

GLfloat heading, xpos, zpos;

GLfloat camx = 0, camy = 0, camz = 0; // camera location.
GLfloat therotate;

GLfloat z =0.0f;




//float xtrans, ztrans, ytrans;
   // GLfloat sceneroty;


   GLfloat xtrans = -xpos;
   GLfloat ztrans = -zpos;
   GLfloat ytrans = -walkbias-0.25f;
   GLfloat sceneroty = 360.0f - yrot;



float CInitGL::Scale = 0.0f;

void CInitGL::setInstance(CInitGL *framework) {
	instance = framework;
}


void CInitGL::StartInitGL(int argc, char** argv)
    {
        glutInit(&argc,argv);
        glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH | GLUT_ALPHA);
      glutInitWindowSize (WINDOW_WIDTH, WINDOW_HEIGHT);
      glutInitWindowPosition(100,100);
      glutCreateWindow("Sector7_0.5.7");
      glutGameModeString("1920x1200@32");
      glutEnterGameMode();

      InitGlutCallbacks();

      pGameCamera = new Ccamera(WINDOW_WIDTH, WINDOW_HEIGHT);

      GLenum res = glewInit();
    if (res != GLEW_OK) {
      fprintf(stderr, "Error: '%s'\n", glewGetErrorString(res));

    }

        glClearColor(0.0f,0.0f,0.0f,0.0f);

        CreateVertexBuffer();
        CreateIndexBuffer();

        CompileShaders();

        glutMainLoop();

    }

void CInitGL::RenderScene()
    {


       pGameCamera->OnRender();

    glClear(GL_COLOR_BUFFER_BIT);
        glTranslatef(xtrans, ytrans, ztrans);

    static float Scale = 0.0f;

    Scale += 0.1f;

    CPipeline p;
    p.Rotate(0.0f, Scale, 0.0f);
    p.WorldPos(0.0f, 0.0f, 3.0f);
    p.SetCamera(pGameCamera->GetPos(), pGameCamera->GetTarget(), pGameCamera->GetUp());
    p.SetPerspectiveProj(60.0f, WINDOW_WIDTH, WINDOW_HEIGHT, 1.0f, 100.0f);

    glUniformMatrix4fv(gWVPLocation, 1, GL_TRUE, (const GLfloat*)p.GetTrans());

    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);

    glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);

    glDisableVertexAttribArray(0);

    glutSwapBuffers();
}


void CInitGL::SpecialKeyboardCB(int Key, int x, int y)
    {
    pGameCamera->OnKeyboard(Key);
    }


 void CInitGL::KeyboardCB(unsigned char Key, int x, int y)
    {
    /*switch (Key) {
        case 'q':
            exit(0);

            /*case GLUT_KEY_PAGE_UP: // tilt up
	z -= 0.2f;
	lookupdown -= 0.2f;
	break;

    /*case GLUT_KEY_PAGE_DOWN: // tilt down
	z += 0.2f;
	lookupdown += 1.0f;
	break;

    case 'w': // walk forward (bob head)
	xpos -= (float)sin(yrot*piover180) * 0.05f;
	zpos -= (float)cos(yrot*piover180) * 0.05f;
	if (walkbiasangle >= 359.0f)
	    walkbiasangle = 0.0f;
	else
	    walkbiasangle+= 10;
	walkbias = (float)sin(walkbiasangle * piover180)/20.0f;
	break;

    case 's': // walk back (bob head)
	xpos += (float)sin(yrot*piover180) * 0.05f;
	zpos += (float)cos(yrot*piover180) * 0.05f;
	if (walkbiasangle <= 1.0f)
	    walkbiasangle = 359.0f;
	else
	    walkbiasangle-= 10;
	walkbias = (float)sin(walkbiasangle * piover180)/20.0f;
	break;

    /*case GLUT_KEY_LEFT: // look left
	yrot += 1.5f;
	break;

    case GLUT_KEY_RIGHT: // look right
	yrot -= 1.5f;
	break;


        }*/

        Ccamera::funcKeyboard(Key, x, y);
    }

 void CInitGL::PassiveMouseCB(int x, int y)
    {
    pGameCamera->OnMouse(x, y);
    }

void CInitGL::InitGlutCallbacks()
    {
        glutDisplayFunc(RenderScene);
        glutIdleFunc(RenderScene);
        glutSpecialFunc(SpecialKeyboardCB);
        glutPassiveMotionFunc(PassiveMouseCB);
        glutKeyboardFunc(KeyboardCB);
    }

void CInitGL::CreateVertexBuffer()
    {
        Vector3f Verticies[4];
        Verticies[0] = Vector3f(-1.0f,-1.0f,0.5773f);
        Verticies[1] = Vector3f(0.0f, -1.0f, -1.15475f);
        Verticies[2] = Vector3f(1.0f, -1.0f, 0.5773f);
        Verticies[3] = Vector3f(0.0f, 1.0f, 0.0f);

        glGenBuffers(1, &VBO);
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(Verticies), Verticies, GL_STATIC_DRAW);

    }


void CInitGL::CreateIndexBuffer()
    {
      unsigned int Indices[] = {
                                    0, 3, 1,
                                    1, 3, 2,
                                    2, 3, 0,
                                    0, 2, 1
                                            };
        glGenBuffers(1, &IBO);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
    }
